#ifndef DATA_TYPES_H
#define DATA_TYPES_H

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <sstream>
#include <Timer.h>
#include <limits>
#include <cmath>


// elementary data structures


#define index unsigned int
#define integer int
#define real float
#define dreal double
#define matrix_type bool

#define INFTY 1.7e308



extern bool VERBOSE;


/* ******************************************************

	simple data structures for vectors and matrices
	based on C arrays

********************************************************* */

template <class T> const T& max (const T& a, const T& b) {
  return (a<b)?b:a;     
}

template <class T> const T& min (const T& a, const T& b) {
  return (a>b)?b:a;     
}


template<class T>
class matrix
{
	public:
	  matrix();
          matrix(size_t m, size_t n, T val);
	  matrix(const matrix<T>& M);
	  ~matrix();

	  size_t rows() const;
          size_t cols() const;

  	  void allocate(size_t m, size_t n, T val);

	 // operators
	 T* operator[](int i);
	 T* operator[](int i) const;

	// access methods
	T* CArrayPtr();

	protected:
	 size_t r,c;
	 T *A;


};

template<class T>
class vector
{
	public:
	  vector();
	  vector(size_t n, T val);
	  vector(const vector<T>& v);
	  ~vector();

	  size_t size() const;
	  void allocate(size_t n, T val);


	 // operators
	 T& operator[](int i);
	 T& operator[](int i) const; 

	// access methods
	T* CArrayPtr();

	protected:
	  size_t n;
	  T* array;

	
};


// implementation

// matrix

template<class T> matrix<T>::matrix() : r(0), c(0), A(NULL){};
template<class T> matrix<T>::~matrix()
{
	if(A) {free(A); }
	
}

template<class T> matrix<T>::matrix(size_t m, size_t n, T val) : r(m), c(n)
{
	A = (T *) calloc(r*c,sizeof(T)); // use C arrays
	for(int idx=0;idx<r*c;idx++) A[idx] = val;
}

template<class T> matrix<T>::matrix(const matrix<T>& M)
{
    if(this==&M) return;
    
     r = M.r; c = M.c; 
     A = (T *) calloc(r*c,sizeof(T));


     for(int idx=0;idx<r*c;idx++) A[idx] = M.A[idx]; 	
} 

template<class T> T* matrix<T>::operator[](int i) 
{
	return (&(A[i*c])); // row start address
}

template<class T> T* matrix<T>::operator[](int i) const
{
	return (&(A[i*c])); // row start address
}

template<class T> std::ostream& operator<<(std::ostream &out, const matrix<T>& M)
{
	for(int i=0;i<M.rows();i++)
        {
		for(int j=0;j<M.cols();j++)
			out << M[i][j] << " ";
		out << std::endl;
	}
	return out;
}


template<class T> size_t matrix<T>::rows() const
{	
	return r;
}

template<class T> size_t matrix<T>::cols() const
{	
	return c;
}


template<class T> void matrix<T>::allocate(size_t m, size_t n, T val)
{
	if(A!=NULL) free(A);	
	r = m; c = n;
	A = (T *) calloc(r*c,sizeof(T));


        for(int idx=0;idx<r*c;idx++) A[idx] = val;	
}

template<class T> T* matrix<T>::CArrayPtr() { return this->A; }


// vectors

template<class T> vector<T>::vector() : n(0), array(NULL) {};

template<class T> vector<T>::~vector() { if(array) free(array); }

template<class T> vector<T>::vector(const vector<T>& v)
{
	if(this==&v) return;
	this->n = v.size();
	for(index i=0;i<n;i++) v.array[i];
}

template<class T> vector<T>::vector(size_t n, T val) : n(n)
{
	array = (T *) calloc(n,sizeof(T)); // use C arrays
	for(int idx=0;idx<n;idx++) array[idx] = val; 	
}

template<class T> T& vector<T>::operator[](int i) 
{
	return (array[i]); // row start address
}

template<class T> T& vector<T>::operator[](int i) const
{
	return (array[i]); // row start address
}

template<class T> std::ostream& operator<<(std::ostream &out, const vector<T>& v)
{
	for(int i=0;i<v.size();i++)
		out << v[i] << " ";
		
	return out;
}

template<class T> size_t vector<T>::size() const
{	
	return n;
}

template<class T> void vector<T>::allocate(size_t n, T val)
{
	if(array!=NULL) free(array);
	this->n = n;
	array = (T *) calloc(n,sizeof(T)); // use C arrays
	for(int idx=0;idx<n;idx++) array[idx] = val; 	
}

template<class T> T* vector<T>::CArrayPtr()
{
	return this->array;
}



#endif
